#include "stdafx.h"

#include <QRect>
#include <QRubberBand>
#include <QMouseEvent>
#include <QHoverEvent>
#include <QApplication>

#include "QFramelessHelper.h"
#include "QFramelessHelper_p.h"


/*****FramelessHelper*****/
QFramelessHelper::QFramelessHelper(QObject *parent)
	: QObject(parent),
	d(new QFramelessHelperPrivate())
{
	d->m_bWidgetMovable = true;
	d->m_bWidgetResizable = true;
	d->m_bRubberBandOnResize = false;
	d->m_bRubberBandOnMove = false;
}

QFramelessHelper::~QFramelessHelper()
{
	QList<QWidget*> keys = d->m_widgetDataHash.keys();
	int size = keys.size();
	for (int i = 0; i < size; ++i)
	{
		delete d->m_widgetDataHash.take(keys[i]);
	}

	delete d;
}

bool QFramelessHelper::eventFilter(QObject *obj, QEvent *event)
{
	switch (event->type())
	{
	case QEvent::MouseMove:
	case QEvent::HoverMove:
	case QEvent::MouseButtonPress:
	case QEvent::MouseButtonRelease:
	case QEvent::Leave:
		{
			WidgetData *data = d->m_widgetDataHash.value(static_cast<QWidget*>(obj));
			if (data)
			{
				data->handleWidgetEvent(event);
				return true;
			}
		}
	}
	return QObject::eventFilter(obj, event);
}

void QFramelessHelper::activateOn(QWidget *topLevelWidget)
{
	if (!d->m_widgetDataHash.contains(topLevelWidget))
	{
		WidgetData *data = new WidgetData(d, topLevelWidget);
		d->m_widgetDataHash.insert(topLevelWidget, data);

		topLevelWidget->installEventFilter(this);
	}
}

void QFramelessHelper::removeFrom(QWidget *topLevelWidget)
{
	WidgetData *data = d->m_widgetDataHash.take(topLevelWidget);
	if (data)
	{
		topLevelWidget->removeEventFilter(this);
		delete data;
	}
}

void QFramelessHelper::setRubberBandOnMove(bool movable)
{
	d->m_bRubberBandOnMove = movable;
	QList<WidgetData*> list = d->m_widgetDataHash.values();
	foreach (WidgetData *data, list)
	{
		data->updateRubberBandStatus();
	}
}

void QFramelessHelper::setWidgetMovable(bool movable)
{
	d->m_bWidgetMovable = movable;
}

void QFramelessHelper::setWidgetResizable(bool resizable)
{
	d->m_bWidgetResizable = resizable;
}

void QFramelessHelper::setRubberBandOnResize(bool resizable)
{
	d->m_bRubberBandOnResize = resizable;
	QList<WidgetData*> list = d->m_widgetDataHash.values();
	foreach (WidgetData *data, list)
	{
		data->updateRubberBandStatus();
	}
}

void QFramelessHelper::setBorderWidth(uint width)
{
	if (width > 0)
	{
		CursorPosCalculator::m_nBorderWidth = width;
	}
}

void QFramelessHelper::setTitleHeight(uint height)
{
	if (height > 0)
	{
		CursorPosCalculator::m_nTitleHeight = height;
	}
}

bool QFramelessHelper::widgetMovable()
{
	return d->m_bWidgetMovable;
}

bool QFramelessHelper::widgetResizable()
{
	return d->m_bWidgetResizable;
}

bool QFramelessHelper::rubberBandOnMove()
{
	return d->m_bRubberBandOnMove;
}

bool QFramelessHelper::rubberBandOnResisze()
{
	return d->m_bRubberBandOnResize;
}

uint QFramelessHelper::borderWidth()
{
	return CursorPosCalculator::m_nBorderWidth;
}

uint QFramelessHelper::titleHeight()
{
	return CursorPosCalculator::m_nTitleHeight;
}


//////////////////////////////////////////////////////////////////////////
int CursorPosCalculator::m_nBorderWidth = 5;
int CursorPosCalculator::m_nTitleHeight = 30;

/***** CursorPosCalculator *****/
CursorPosCalculator::CursorPosCalculator()
{
	reset();
}

void CursorPosCalculator::reset()
{
	m_bOnEdges = false;
	m_bOnLeftEdge = false;
	m_bOnRightEdge = false;
	m_bOnTopEdge = false;
	m_bOnBottomEdge = false;
	m_bOnTopLeftEdge = false;
	m_bOnBottomLeftEdge = false;
	m_bOnTopRightEdge  = false;
	m_bOnBottomRightEdge = false;
}

void CursorPosCalculator::recalculate(const QPoint &gMousePos, const QRect &frameRect)
{
	int globalMouseX = gMousePos.x();
	int globalMouseY = gMousePos.y();

	int frameX = frameRect.x();
	int frameY = frameRect.y();

	int frameWidth = frameRect.width();
	int frameHeight = frameRect.height();

	m_bOnLeftEdge = (globalMouseX >= frameX &&
		globalMouseX <= frameX + m_nBorderWidth );


	m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth &&
		globalMouseX <= frameX + frameWidth);

	m_bOnTopEdge = (globalMouseY >= frameY &&
		globalMouseY <= frameY + m_nBorderWidth );

	m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth &&
		globalMouseY <= frameY + frameHeight);

	m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge;
	m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge;
	m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge;
	m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge;

	m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge;
}



//////////////////////////////////////////////////////////////////////////
/***** WidgetData *****/
WidgetData::WidgetData(QFramelessHelperPrivate *_d, QWidget *pTopLevelWidget)
{
	d = _d;
	m_pWidget = pTopLevelWidget;
	m_bLeftButtonPressed = false;
	m_bCursorShapeChanged = false;
	m_bLeftButtonTitlePressed = false;
	m_pRubberBand = NULL;

	m_windowFlags = m_pWidget->windowFlags();
	m_pWidget->setMouseTracking(true);
	m_pWidget->setAttribute(Qt::WA_Hover, true);

	updateRubberBandStatus();
}

WidgetData::~WidgetData()
{
	m_pWidget->setMouseTracking(false);
	m_pWidget->setWindowFlags(m_windowFlags);
	m_pWidget->setAttribute(Qt::WA_Hover, false);

	delete m_pRubberBand;
	m_pRubberBand = NULL;
}

QWidget* WidgetData::widget()
{
	return m_pWidget;
}

void WidgetData::handleWidgetEvent(QEvent *event)
{
	switch (event->type())
	{
	default:
		break;
	case QEvent::MouseButtonPress:
		handleMousePressEvent(static_cast<QMouseEvent*>(event));
		break;
	case QEvent::MouseButtonRelease:
		handleMouseReleaseEvent(static_cast<QMouseEvent*>(event));
		break;
	case QEvent::MouseMove:
		handleMouseMoveEvent(static_cast<QMouseEvent*>(event));
		break;
	case QEvent::Leave:
		handleLeaveEvent(static_cast<QMouseEvent*>(event));
		break;
	case QEvent::HoverMove:
		handleHoverMoveEvent(static_cast<QHoverEvent*>(event));
		break;
	}
}

void WidgetData::updateRubberBandStatus()
{
	if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize)
	{
		if (NULL == m_pRubberBand)
			m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
	}
	else
	{
		delete m_pRubberBand;
		m_pRubberBand = NULL;
	}
}

void WidgetData::updateCursorShape(const QPoint &gMousePos)
{
	if (m_pWidget->isFullScreen() || m_pWidget->isMaximized())
	{
		if (m_bCursorShapeChanged)
		{
			m_pWidget->unsetCursor();
		}
		return;
	}

	m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry());

	if(m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge)
	{
		m_pWidget->setCursor( Qt::SizeFDiagCursor );
		m_bCursorShapeChanged = true;
	}
	else if(m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge)
	{
		m_pWidget->setCursor( Qt::SizeBDiagCursor );
		m_bCursorShapeChanged = true;
	}
	else if(m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge)
	{
		m_pWidget->setCursor( Qt::SizeHorCursor );
		m_bCursorShapeChanged = true;
	}
	else if(m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge)
	{
		m_pWidget->setCursor( Qt::SizeVerCursor );
		m_bCursorShapeChanged = true;
	}
	else
	{
		if (m_bCursorShapeChanged)
		{
			m_pWidget->unsetCursor();
			m_bCursorShapeChanged = false;
		}
	}
}

void WidgetData::resizeWidget(const QPoint &gMousePos)
{
	QRect origRect;

	if (d->m_bRubberBandOnResize)
		origRect = m_pRubberBand->frameGeometry();
	else
		origRect = m_pWidget->frameGeometry();

	int left = origRect.left();
	int top = origRect.top();
	int right = origRect.right();
	int bottom = origRect.bottom();
	origRect.getCoords(&left, &top, &right, &bottom);

	int minWidth = m_pWidget->minimumWidth();
	int minHeight = m_pWidget->minimumHeight();

	if (m_pressedMousePos.m_bOnTopLeftEdge)
	{
		left = gMousePos.x();
		top = gMousePos.y();
	}
	else if (m_pressedMousePos.m_bOnBottomLeftEdge)
	{
		left = gMousePos.x();
		bottom = gMousePos.y();
	}
	else if (m_pressedMousePos.m_bOnTopRightEdge)
	{
		right = gMousePos.x();
		top = gMousePos.y();
	}
	else if (m_pressedMousePos.m_bOnBottomRightEdge)
	{
		right = gMousePos.x();
		bottom = gMousePos.y();
	}
	else if (m_pressedMousePos.m_bOnLeftEdge)
	{
		left = gMousePos.x();
	}
	else if (m_pressedMousePos.m_bOnRightEdge)
	{
		right = gMousePos.x();
	}
	else if (m_pressedMousePos.m_bOnTopEdge)
	{
		top = gMousePos.y();
	}
	else if (m_pressedMousePos.m_bOnBottomEdge)
	{
		bottom = gMousePos.y();
	}

	QRect newRect(QPoint(left, top), QPoint(right, bottom));

	if (newRect.isValid())
	{
		if (minWidth > newRect.width())
		{
			if (left != origRect.left())
				newRect.setLeft(origRect.left());
			else
				newRect.setRight(origRect.right());
		}
		if (minHeight > newRect.height())
		{
			if (top != origRect.top())
				newRect.setTop(origRect.top());
			else
				newRect.setBottom(origRect.bottom());
		}

		if (d->m_bRubberBandOnResize)
		{
			m_pRubberBand->setGeometry(newRect);
		}
		else
		{
			m_pWidget->setGeometry(newRect);
		}
	}
}

void WidgetData::moveWidget(const QPoint& gMousePos)
{
	if (d->m_bRubberBandOnMove)
	{
		m_pRubberBand->move(gMousePos - m_ptDragPos);
	}
	else
	{
		m_pWidget->move(gMousePos - m_ptDragPos);
	}
}

void WidgetData::handleMousePressEvent(QMouseEvent *event)
{
	if (event->button() == Qt::LeftButton)
	{
		m_bLeftButtonPressed = true;
		m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight;

		QRect frameRect = m_pWidget->frameGeometry();
		m_pressedMousePos.recalculate(event->globalPos(), frameRect);

		m_ptDragPos = event->globalPos() - frameRect.topLeft();

		if (m_pressedMousePos.m_bOnEdges)
		{
			if (d->m_bRubberBandOnResize)
			{
				m_pRubberBand->setGeometry(frameRect);
				m_pRubberBand->show();
			}
		}
		else if (d->m_bRubberBandOnMove)
		{
			m_pRubberBand->setGeometry(frameRect);
			m_pRubberBand->show();
		}
	}
}

void WidgetData::handleMouseReleaseEvent(QMouseEvent *event)
{
	if (event->button() == Qt::LeftButton)
	{
		m_bLeftButtonPressed = false;
		m_bLeftButtonTitlePressed = false;
		m_pressedMousePos.reset();
		if (m_pRubberBand && m_pRubberBand->isVisible())
		{
			m_pRubberBand->hide();
			m_pWidget->setGeometry(m_pRubberBand->geometry());
		}
	}
}

void WidgetData::handleMouseMoveEvent(QMouseEvent *event)
{
	if (m_bLeftButtonPressed)
	{
		if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges)
		{
			resizeWidget(event->globalPos());
		}
		else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed)
		{
			moveWidget(event->globalPos());
		}
	}
	else if (d->m_bWidgetResizable)
	{
		updateCursorShape(event->globalPos());
	}
}

void WidgetData::handleLeaveEvent(QEvent *event)
{
	Q_UNUSED(event)
		if (!m_bLeftButtonPressed)
		{
			m_pWidget->unsetCursor();
		}
}

void WidgetData::handleHoverMoveEvent(QHoverEvent *event)
{
	if (d->m_bWidgetResizable)
	{
		updateCursorShape(m_pWidget->mapToGlobal(event->pos()));
	}
}

